﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using System.Windows.Media;

namespace ChromaCode.Extensions
{
    /// <summary>
    /// Extensions for XDocuments and XElements to make loading XML data easier
    /// </summary>
    internal static class XMLExtensions
    {
        /// <summary>
        /// Return a value for a specified attribute as a string
        /// </summary>
        /// <param name="element">The element that contains the attribute we want</param>
        /// <param name="valueName">The name of the attribute to return</param>
        /// <param name="defaultValue">The default value to return if the attribute does not exist</param>
        /// <returns>A string containing the value of the attribute</returns>
        public static string GetAttributeStringValue(this XElement element, string valueName, string defaultValue)
        {
            string result = defaultValue;

            if (element != null)
            {
                // Search for the child attribute
                XAttribute attribute = element.Attributes().Where(x => x.Name == valueName).FirstOrDefault();
                if (attribute != null)
                {
                    result = attribute.Value;
                }
            }

            return result;
        }

        /// <summary>
        /// Return a value for a specified attribute as an integer
        /// </summary>
        /// <param name="element">The element that contains the attribute we want</param>
        /// <param name="valueName">The name of the attribute to return</param>
        /// <param name="defaultValue">The default value to return if the attribute does not exist or is not numeric</param>
        /// <returns>An integer containing the value of the attribute</returns>
        public static int GetAttributeIntValue(this XElement element, string valueName, int defaultValue)
        {
            int result = defaultValue;

            if (element != null)
            {
                // Search for the child attribute
                XAttribute attribute = element.Attributes().Where(x => x.Name == valueName).FirstOrDefault();
                if (attribute != null)
                {
                    int.TryParse(attribute.Value, out result);
                }
            }

            return result;
        }

        /// <summary>
        /// Return a value for a specified attribute as a boolean
        /// </summary>
        /// <param name="element">The element that contains the attribute we want</param>
        /// <param name="valueName">The name of the attribute to return</param>
        /// <param name="defaultValue">The default value to return if the attribute does not exist</param>
        /// <returns>A brush containing the colour information specified in the attribute</returns>
        public static Brush GetAttributeBrushValue(this XElement element, string valueName, Brush defaultValue)
        {
            // Set the initial return value to the default
            Brush result = defaultValue;

            if (element != null)
            {
                // Search for the child attribute
                XAttribute attribute = element.Attributes().Where(x => x.Name == valueName).FirstOrDefault();

                // If the child attribute exists then convert it to a "Brush" if possible
                if (attribute != null)
                {
                    try
                    {
                        // Convert the colour name or number into a brush
                        BrushConverter brushConverter = new BrushConverter();
                        result = brushConverter.ConvertFromString(attribute.Value.ToLower()) as Brush;
                    }
                    catch (Exception)
                    {
                        // Cannot do the conversion so return a black brush
                        result = Brushes.Black;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Return the value for a specified attribute on an element as a boolean
        /// </summary>
        /// <param name="attribute">The element that contains the value we want</param>
        /// <param name="valueName">The name of the attribute to return</param>
        /// <param name="defaultValue">The default value to return if the attribute does not exist</param>
        /// <returns>A boolean containing the value of the child attribute</returns>
        public static bool GetAttributeBoolValue(this XElement element, string valueName, bool defaultValue)
        {
            // Set the initial return value to the default
            bool result = defaultValue;

            if (element != null)
            {
                // Search for the child attribute
                XAttribute attribute = element.Attributes().Where(x => x.Name == valueName).FirstOrDefault();

                // If the child attribute exists then convert it from a string to a boolean and return the element
                if (attribute != null)
                {
                    switch (attribute.Value.ToLower())
                    {
                        case "true":
                        case "t":
                        case "1":
                        {
                            result = true;
                            break;
                        }

                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Return the list of items in the child elements value data as a list of strings
        /// </summary>
        /// <param name="element">The element containing the child element we want to return the list of strings for</param>
        /// <param name="childElementName">The name of the child element whoes value we will convert to a list of strings</param>
        /// <returns>The child elements data as a list of items</returns>
        public static List<string> GetElementValueStringList(this XElement element, string childElementName)
        {
            List<string> result = new List<string>();

            if (element != null)
            {
                // Search for the child element
                XElement childElement = element.Descendants().Where(x => x.Name == childElementName).FirstOrDefault();

                if (childElement != null)
                {
                    // Split the value for the child element into an array of strings and add it to the element. First
                    // strip out carriage return line feed combinations
                    string rawData = childElement.Value.Replace("\r\n", string.Empty);
                    rawData = rawData.Replace("\n", string.Empty);
                    rawData = rawData.Replace("\r", string.Empty);
                    string[] data = rawData.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
                    result.AddRange(data);
                }
            }
            return result;
        }

        /// <summary>
        /// Return the list of items in the child elements value data as a list of strings
        /// </summary>
        /// <param name="element">The element containing the child element we want to return the list of strings for</param>
        /// <param name="childElementName">The name of the child element whoes value we will convert to a list of strings</param>
        /// <returns>The child elements data as a list of items</returns>
        public static List<char> GetElementValueCharacterList(this XElement element, string childElementName)
        {
            List<char> result = new List<char>();

            if (element != null)
            {
                // Search for the child element
                XElement childElement = element.Descendants().Where(x => x.Name == childElementName).FirstOrDefault();

                if (childElement != null)
                {
                    // Split the value for the child element into an array of strings and add it to the element. First
                    // strip out carriage return line feed combinations
                    string rawData = childElement.Value.Replace("\r\n", string.Empty);
                    rawData = rawData.Replace("\n", string.Empty);
                    rawData = rawData.Replace("\r", string.Empty);
                    string[] data = rawData.Split(" ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);

                    // Loop through the data and convert it to a list of characters
                    char charToAdd;
                    foreach (string dataItem in data)
                    {
                        if (char.TryParse(dataItem, out charToAdd))
                        {
                            result.Add(charToAdd);
                        }
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Return the value from a child element of the supplied element as a string , but only of the child element exists
        /// </summary>
        /// <param name="element">The element that contains the child element we want</param>
        /// <param name="valueName">The name of the value to return</param>
        /// <returns>A string containing either the child elements data, or a blank string</returns>
        public static string GetChildElementStringValue(this XElement element, string valueName)
        {
            string result = string.Empty;

            if (element != null)
            {
                // Search for the child element and return its value if it exists
                XElement childElement = element.Elements().Where(x => x.Name == valueName).FirstOrDefault();
                if (childElement != null)
                {
                    result = childElement.Value;
                }
            }

            return result;
        }
    }
}
